package ro.myApp.esb.service;

import static ro.myApp.esb.service.EsbConfigBuilder.getConfigFiles;

import java.security.Principal;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;

import org.mule.api.MuleContext;
import org.mule.api.MuleException;
import org.mule.api.MuleMessage;
import org.mule.config.spring.SpringXmlConfigurationBuilder;
import org.mule.context.DefaultMuleContextFactory;
import org.mule.module.client.MuleClient;
import org.mule.transport.NullPayload;

import ro.myApp.commons.dto.BaseDtoMessage;
import ro.myApp.commons.dto.QueryMessage;
import ro.myApp.commons.service.EsbService;
import ro.myApp.commons.service.EsbServiceLocal;

@Stateless
@Remote(value = EsbService.class)
@Local(value = EsbServiceLocal.class)
public class EsbServiceImpl implements EsbService, EsbServiceLocal {

	/**
	 * 
	 */
	private static final long serialVersionUID = -7926571810082676484L;

	@Resource
	protected SessionContext ctx;
	
	static MuleContext muleContext;
	
	private MuleClient muleClient;
	
	
	static {
		try {
			DefaultMuleContextFactory muleContextFactory = new DefaultMuleContextFactory();
			SpringXmlConfigurationBuilder configBuilder = new SpringXmlConfigurationBuilder(getConfigFiles());
			muleContext = muleContextFactory.createMuleContext(configBuilder);
			muleContext.start();
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
	
	@PostConstruct
	public void init(){
		initMuleClient();
	}
	 
	@Override
	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	public Object sendMessageSynchronous(BaseDtoMessage message) 
			throws Throwable {
		if (message instanceof QueryMessage) {
			return sendMessageSynchronousTransactionNotSupported(message);
		}
		return sendMessageSynchronousTransactionRequired(message);		
	}

	@Override
	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	public Object sendMessageSynchronousTransactionNotSupported(BaseDtoMessage message)
			throws Throwable {
		return dispatchMessage(message, TransactionAttributeType.NOT_SUPPORTED);
	}

	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public Object sendMessageSynchronousTransactionRequired(BaseDtoMessage message)
			throws Throwable {
		return dispatchMessage(message, TransactionAttributeType.REQUIRED);
	}

	@Override
	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	public Object sendMessageSynchronousNewTransaction(BaseDtoMessage message)
			throws Throwable {
		return dispatchMessage(message, TransactionAttributeType.REQUIRES_NEW);
	}

	public String getUserNameConnected() {
		try {
			return ctx.getCallerPrincipal().getName();
		} catch (Throwable t) {
			t.printStackTrace();
			return "guest";
		}
	}

	public Principal getPrincipal() {
		return ctx.getCallerPrincipal();
	}

	private void setUserOnMessage(BaseDtoMessage message){
		if (null==message.getUserName())
			message.setUserName(getUserNameConnected());
	}
	
	private void initMuleClient() {
		try {
			this.muleClient = new MuleClient();
		} catch (MuleException e) {
			e.printStackTrace();
		}
	}
	
	private Object dispatchMessage(BaseDtoMessage message,
			TransactionAttributeType transactionType) throws Throwable {
		if (muleClient != null) {
			if (!muleClient.getMuleContext().equals(muleContext)) {
				initMuleClient();
			}
			
			setUserOnMessage(message);

			Map<String, Object> props = new HashMap<String, Object>();
			props.put("TransactionType", transactionType);

			StringBuffer channel = new StringBuffer();
			channel.append("vm://" + message.getClass().getName());

			MuleMessage result = muleClient.send(channel.toString(), message, props);
			if (result.getExceptionPayload() != null) {
				throw result.getExceptionPayload().getRootException();
			} else if (result.getPayload() instanceof NullPayload) {
				return null;
			} else {
				return result.getPayload();
			}
		} else {
			throw new Exception("Mule client is not available");
		}
	}
}
